#include "ZTextInput.h"
#include "ZControlFactory.h"
#include "ZPaintManager.h"
#include "ZWindow.h"
#include "common/ZTranslation.h"

namespace ZuiLib
{
#define CARET_OFFSET_Y 8


ZTextInput::ZTextInput(void)
{
	m_uButtonState=0;
	m_bMouseEnabled=true;
	m_caret=new ZRefCountedObject<ZCaret>(this);
	m_selBegin=0;
	m_selEnd=0;
	m_scroll=0;
	m_caretPos=0;

	m_editingBegin=0;
	m_editingEnd=0;
	m_editingOffset=0;

	m_needResetText=true;
	m_dwTextStyle = DT_VCENTER;

	m_password = false;
	m_passwordChar = '*';

	m_readOnly = false;
}


ZTextInput::~ZTextInput(void)
{
	m_caret=NULL;
}


const char* ZTextInput::GetClass() const
{
	return "TextInput";
}


void* ZTextInput::GetInterface(const char* pstrName)
{
	if( strcmp(pstrName, "TextInput") == 0 ) 
		return static_cast<ZTextInput*>(this);
	else
		return ZLabel::GetInterface(pstrName);
}

void ZTextInput::SetPos(RECT rcItem)
{
	ZLabel::SetPos(rcItem);

	if(IsFocused())
	{
		RECT rcCaret=GetCaretRect();
		m_caret->SetPos(rcCaret);
		m_caret->ShowCaret(true);

		ZRect rc(rcCaret);
		rc.y+=_dpi_scale(CARET_OFFSET_Y);

		ZScale scale = GetWindow()->GetRenderScale();
		rc.x = rc.x * scale.den / scale.num;
		rc.y = rc.y * scale.den / scale.num;
		rc.w = rc.w * scale.den / scale.num;
		rc.h = rc.h * scale.den / scale.num;
		SDL_SetTextInputRect(&rc);
	}
}


ZString ZTextInput::GetText() const
{
	return m_doc.GetText();
}


void ZTextInput::SetText(const char* buf)
{
	m_doc.SetText(buf);
	UpdateText();
}

void ZTextInput::SetTipText(const char* tipText)
{
	m_tipText = tipText;
	if (m_doc.IsEmpty())
		Invalidate();
}

void ZTextInput::SetReadOnly(bool readonly) {
	m_readOnly = readonly;
	m_doc.SetReadOnly(readonly);
}

void ZTextInput::Copy()
{
	ZString textUtf8;
	if(m_selEnd != m_selBegin)
	{
		int selBegin=m_selBegin;
		int selEnd=m_selEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_selEnd;
			selEnd=m_selBegin;
		}

		ZWString text= m_doc.GetRange(selBegin,selEnd);
		Utf16ToUtf8(&text,&textUtf8);
	}
	SDL_SetClipboardText(textUtf8.GetString());
}


void ZTextInput::Cut()
{
	if (m_readOnly)
		return;

	ZString textUtf8;
	if(m_selEnd != m_selBegin)
	{
		int selBegin=m_selBegin;
		int selEnd=m_selEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_selEnd;
			selEnd=m_selBegin;
		}

		ZWString text= m_doc.GetRange(selBegin,selEnd);
		Utf16ToUtf8(&text,&textUtf8);
	}
	SDL_SetClipboardText(textUtf8.GetString());

	if(m_selBegin!=m_selEnd)
	{
		RemoveSel();
		int scrollMax=GetScrollMax();
		if (m_scroll>scrollMax)
		{
			m_scroll=scrollMax;
		}
		UpdateText();
		SignalTextChanged(this);
	}
}

void ZTextInput::Undo() {
	if (m_readOnly)
		return;

	m_caretPos = m_doc.Undo();
	if (m_caretPos >= 0) {
		m_selBegin = m_caretPos;
		m_selEnd = m_caretPos;

		int scrollMax = GetScrollMax();
		if (m_scroll>scrollMax)
		{
			m_scroll = scrollMax;
		}
		UpdateText();
		SignalTextChanged(this);
	}

}

void ZTextInput::Paste()
{
	if (m_readOnly)
		return;

	if (!SDL_HasClipboardText())
		return;

	char* textUtf8=SDL_GetClipboardText();
	ZWString utf16(textUtf8);
	SDL_free(textUtf8);
	RemoveSel();

	ZWString temp;
	const ZWChar* str=utf16.GetString();

	while (*str)
	{
		if (*str!='\r' && *str!='\n')
		{
			temp.Append(*str);
		}
		++str;
	}

	m_doc.Insert(m_caretPos,temp);
	m_caretPos+=temp.GetLength();
	UpdateText();

	SignalTextChanged(this);
}

void ZTextInput::SelAll()
{
	m_selBegin=0;
	m_selEnd= m_doc.GetLength();
	Invalidate();
}

void ZTextInput::SelNone()
{
	m_selBegin=m_caretPos;
	m_selEnd=m_caretPos;
	Invalidate();
}

void ZTextInput::DoEvent(TEventUI& event)
{
	switch(event.Type)
	{
	case UIEVENT_MOUSEENTER:
		{
			GetWindow()->SetCursor(SDL_SYSTEM_CURSOR_IBEAM);
			break;
		}
	case UIEVENT_MOUSELEAVE:
		{
			GetWindow()->SetCursor(SDL_SYSTEM_CURSOR_ARROW);
			break;
		}

	case UIEVENT_SETFOCUS:
		{
			RECT rcCaret=GetCaretRect();
			m_caret->SetPos(rcCaret);
			m_caret->ShowCaret(true);

			ZRect rc(rcCaret);
			SDL_StartTextInput();
			rc.y+=_dpi_scale(CARET_OFFSET_Y);
			ZScale scale = GetWindow()->GetRenderScale();
			rc.x = rc.x * scale.den / scale.num;
			rc.y = rc.y * scale.den / scale.num;
			rc.w = rc.w * scale.den / scale.num;
			rc.h = rc.h * scale.den / scale.num;
			SDL_SetTextInputRect(&rc);
		}
		break;

	case UIEVENT_KILLWINDOWFOCUS:
		if (m_pManager)
			m_pManager->SetFocus(NULL);
	case UIEVENT_KILLFOCUS:
		m_caret->ShowCaret(false);
		SDL_StopTextInput();
		break;

	case UIEVENT_BUTTONDOWN:
		{
			SetCaretPos(event.ptMouse);

			m_selBegin=m_caretPos;
			m_selEnd=m_caretPos;

			m_editingBegin=m_caretPos;
			m_editingEnd=m_caretPos;
			m_editingOffset=0;

			m_uButtonState|=UISTATE_PUSHED;
			Invalidate();
		}
		break;

	case UIEVENT_BUTTONUP:
		{
			m_uButtonState &= ~UISTATE_PUSHED;
		}
		break;

	case UIEVENT_MOUSEMOVE:
		{
			if ((m_uButtonState & UISTATE_PUSHED)!=0)
			{
				m_selEnd=CharFromPos(event.ptMouse);
				Invalidate();
			}
		}
		break;

	case UIEVENT_KEYDOWN:
		{
			SDL_Keysym* key=(SDL_Keysym*)event.wParam;
			switch(key->sym)
			{
			case SDLK_BACKSPACE:
				if (m_editingBegin==m_editingEnd && !m_readOnly)
				{
					if (m_doc.IsEmpty())
						return;
					if (m_selBegin==m_selEnd)
					{
						if(m_caretPos>0)
							m_doc.Remove(--m_caretPos);
					}
					else
					{
						RemoveSel();
					}

					int scrollMax=GetScrollMax();
					if (m_scroll>scrollMax)
					{
						m_scroll=scrollMax;
					}
					UpdateText();
					SignalTextChanged(this);
				}
				break;

			case SDLK_DELETE:
				if (m_editingBegin==m_editingEnd && !m_readOnly)
				{
					if (m_doc.IsEmpty())
						return;
					if (m_selBegin==m_selEnd)
					{
						if(m_caretPos>0)
							m_doc.Remove(m_caretPos);
					}
					else
					{
						RemoveSel();
					}

					int scrollMax=GetScrollMax();
					if (m_scroll>scrollMax)
					{
						m_scroll=scrollMax;
					}
					UpdateText();
					SignalTextChanged(this);
				}
				break;
			case SDLK_RETURN:
				{
					SignalReturn(this);
				}
				break;
			case SDLK_ESCAPE:
				{
					SignalEscape(this);
				}
				break;
			case SDLK_LEFT:
				m_selBegin=m_caretPos;
				m_selEnd=m_caretPos;
				if (m_caretPos>0 )
				{
					--m_caretPos;
					UpdateScroll();
				}
				break;
			case SDLK_RIGHT:
				if (m_caretPos<m_doc.GetLength())
				{
					m_selBegin=m_selEnd;
					++m_caretPos;
					UpdateScroll();
				}
				break;
			case SDLK_UP:
				{
						SignalUpDown(this, true);
				}
				break;
			case SDLK_DOWN:
				{
						SignalUpDown(this, false);
				}
				break;
			case SDLK_c:
				{
					if (CheckKeyState(KS_CTRL))
					{
						Copy();
					}
				}
				break;

			case SDLK_x:
				{
					if (CheckKeyState(KS_CTRL))
					{
						Cut();
					}
				}
				break;

			case SDLK_v:
				{
					if (CheckKeyState(KS_CTRL))
					{
						Paste();
					}
				}
				break;
			case SDLK_a:
				{
					if (CheckKeyState(KS_CTRL))
					{
						SelAll();
					}
				}
				break;
			case SDLK_z:
			{
				if (CheckKeyState(KS_CTRL))
				{
					Undo();
				}
			}
			break;
			}
		}
		break;
	case UIEVENT_TEXTINPUT:
		if(!m_readOnly)
		{
			SDL_TextInputEvent* sdlEv=(SDL_TextInputEvent*)event.wParam;
			ZString utf8=sdlEv->text;
			ZWString utf16;

			RemoveSel();
			RemoveEditing();

			Utf8ToUtf16(&utf8,&utf16);
			m_doc.Insert(m_caretPos,utf16);
			m_caretPos+=utf16.GetLength();
			UpdateScroll();
			SignalTextChanged(this);
		}
		break;
	case UIEVENT_TEXTEDITING:
		if(!m_readOnly)
		{
			SDL_TextEditingEvent* sdlEv=(SDL_TextEditingEvent*)event.wParam;
			ZString utf8(sdlEv->text);
			ZWString utf16;
			Utf8ToUtf16(&utf8,&utf16);

			RemoveSel();
			RemoveEditing();

			int len=utf16.GetLength();
			m_editingBegin=m_caretPos;
			m_editingEnd=m_editingBegin+len;

			m_editingOffset=ZMIN(sdlEv->start,len);

			m_doc.InsertEditing(m_caretPos,utf16);
			m_caretPos+=m_editingOffset;

			int scrollMax=GetScrollMax();
			if (m_scroll>scrollMax)
			{
				m_scroll=scrollMax;
			}

			UpdateScroll();
		}
		break;
	}
}


void ZTextInput::RemoveSel()
{
	if(m_selBegin!=m_selEnd)
	{
		int selBegin=m_selBegin;
		int selEnd=m_selEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_selEnd;
			selEnd=m_selBegin;
		}

		m_doc.Remove(selBegin,selEnd-selBegin);
		m_selEnd=selBegin;
		m_selBegin=selBegin;
		m_caretPos=selBegin;
	}
}

void ZTextInput::RemoveEditing()
{
	if(m_editingBegin!=m_editingEnd)
	{
		int selBegin=m_editingBegin;
		int selEnd=m_editingEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_editingEnd;
			selEnd=m_editingBegin;
		}

		m_doc.RemoveEditing(selBegin,selEnd-selBegin);
		m_editingEnd=selBegin;
		m_editingBegin=selBegin;
		m_caretPos=selBegin;
	}
}

void ZTextInput::UpdateText()
{
	m_needResetText=true;
	Invalidate();
}

int ZTextInput::GetScrollMax()
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	//caretPos>scroll 计算滚动位置
	ZFreeTypeFontPtr font =GetRenerder()->FindFont(m_sFont,m_nFontSize);
	if (font)
	{
		int width=0;
		for (int i= m_doc.GetLength()-1;i>=0;--i)
		{
			int w = font->GetWidth(m_password ? m_passwordChar : m_doc.GetAt(i));
			if (width+w> rc.right-rc.left)
			{
				return i+1;
				break;
			}
			else
				width+=w;
		}
	}
	return 0;
}


RECT ZTextInput::GetCaretRect()
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	ZWString textDraw= m_doc.GetRange(m_scroll);
	ZFreeTypeFontPtr font = GetRenerder()->FindFont(m_sFont,m_nFontSize);
	if (font)
	{
		int caretPos=rc.left;
		int width=0;
		for (int i=0;i<textDraw.GetLength();++i)
		{
			int w = font->GetWidth(m_password?m_passwordChar:textDraw[i]);
			if (width+w > rc.right-rc.left || m_caretPos==i+m_scroll)
			{
				break;
			}
			else
			{
				width+=w;
				caretPos+=w;
			}
		}

		RECT rcCaret={caretPos,rc.top+(rc.bottom-rc.top - font->GetLineHeight())/2,caretPos + _dpi_scale(1),0};
		rcCaret.bottom=rcCaret.top+font->GetLineHeight();
		return rcCaret;
	}
	else
	{
		RECT rcCaret={rc.left,rc.top,rc.left + _dpi_scale(1),rc.bottom};
		return rcCaret;
	}
}

void ZTextInput::SetCaretPos(POINT pt)
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	ZWString textDraw= m_doc.GetRange(m_scroll);
	ZFreeTypeFontPtr font = GetRenerder()->FindFont(m_sFont,m_nFontSize);
	if (font)
	{
		int caretPos=rc.left;
		int width=0;
		int offset=pt.x-rc.left;
		int caret=m_scroll;
		for (int i=0;i<textDraw.GetLength();++i)
		{
			int w = font->GetWidth(m_password ? m_passwordChar : textDraw[i]);
			if (width+w > offset)
			{
				if( offset>(width+w/2))
				{
					++caret;
					caretPos+=w;
				}
				break;
			}
			else
			{
				++caret;
				width+=w;
				caretPos+=w;
			}
		}
		m_caretPos=caret;
		RECT rcCaret={caretPos,rc.top+(rc.bottom-rc.top-font->GetLineHeight())/2,caretPos+ _dpi_scale(1),0};
		rcCaret.bottom=rcCaret.top+font->GetLineHeight();
		m_caret->SetPos(rcCaret);
		m_caret->ShowCaret(true);

		ZRect rc(rcCaret);
		SDL_StartTextInput();
		rc.y+=_dpi_scale(CARET_OFFSET_Y);
		ZScale scale = GetWindow()->GetRenderScale();
		rc.x = rc.x * scale.den / scale.num;
		rc.y = rc.y * scale.den / scale.num;
		rc.w = rc.w * scale.den / scale.num;
		rc.h = rc.h * scale.den / scale.num;
		SDL_SetTextInputRect(&rc);
	}
}


void ZTextInput::UpdateScroll()
{
	if(m_caretPos<m_scroll)
		UpdateText();
	else
	{
		RECT rc = m_rcItem;
		InsetRect(&rc, &m_rcTextPadding);
		//caretPos>scroll 计算滚动位置
		ZWString textDraw= m_doc.GetRange(m_scroll,m_caretPos);
		ZFreeTypeFontPtr font = GetRenerder()->FindFont(m_sFont,m_nFontSize);
		if (font)
		{
			int width=0;
			for (int i=textDraw.GetLength()-1;i>=0;--i)
			{
				int w=font->GetWidth(m_password ? m_passwordChar : textDraw[i]);
				if (width+w> rc.right-rc.left)
				{
					m_scroll=m_scroll+i+1;
					break;
				}
				else
					width+=w;
			}
		}
		UpdateText();
	}
}

void ZTextInput::SetAttribute(const char* pstrName, const char* pstrValue)
{
	if (stricmp(pstrName, "tiptext") == 0)
	{
		const ZString* text = ZTranslation::Instance()->TranslateText(pstrValue);
		if (text)
			SetTipText(*text);
		else
			SetTipText(pstrValue);
	}
	else if (stricmp(pstrName, "password") == 0)
	{
		m_password = ParserBool(pstrValue);
	}
	else if (stricmp(pstrName, "passwordchar") == 0)
	{
		m_passwordChar = pstrValue[0];
	}
	else if (stricmp(pstrName, "caretcolor") == 0)
	{
		m_caret->SetColor(TextToColor(pstrValue));
	}
	else if (stricmp(pstrName, "onTextChanged") == 0)
	{
		SetEventAttr("onTextChanged", pstrValue);
		SignalTextChanged.connect((ZLuaWindow*)GetWindow(), &ZLuaWindow::onTextInputChanged);
	}
	else if (stricmp(pstrName, "onReturn") == 0)
	{
		SetEventAttr("onReturn", pstrValue);
		SignalReturn.connect((ZLuaWindow*)GetWindow(), &ZLuaWindow::onTextInputReturn);
	}
	else if (stricmp(pstrName, "onEscape") == 0)
	{
		SetEventAttr("onEscape", pstrValue);
		SignalEscape.connect((ZLuaWindow*)GetWindow(), &ZLuaWindow::onTextInputEscape);
	}
	else if (stricmp(pstrName, "onUpDown") == 0)
	{
		SetEventAttr("onUpDown", pstrValue);
		SignalUpDown.connect((ZLuaWindow*)GetWindow(), &ZLuaWindow::onUpDown);
	}
	else
		ZLabel::SetAttribute(pstrName, pstrValue);
}


void ZTextInput::PaintText(ZRenderer* render)
{
	if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor();
    if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor();

	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	color_t color=m_dwTextColor;

	if( !IsEnabled() ) {
		color=m_dwDisabledTextColor;
	}

	if (m_needResetText)
	{
		if (m_caretPos<m_scroll)
		{
			m_scroll=m_caretPos;
		}

		ZWString textDraw= m_doc.GetRange(m_scroll);
		if (m_password) {
			//字符替换为*
			for (int i = 0; i < textDraw.GetLength(); ++i) {
				textDraw.SetAt(i,m_passwordChar);
			}
		}

		ZFreeTypeFontPtr font=GetFont();
		if (font)
		{
			int caretPos=rc.left;
			int width=0;
			//从右侧开始计算scroll
			for (int i=0;i<textDraw.GetLength();++i)
			{
				int w = font->GetWidth(textDraw[i]);
				if (width+ w> rc.right-rc.left)
				{
					textDraw=textDraw.Left(i);
					break;
				}
				else
					width+= w;

				if(m_scroll+i <m_caretPos)
					caretPos+= w;
			}
			
			RECT rcCaret={caretPos,rc.top+(rc.bottom-rc.top-font->GetLineHeight()-2)/2,caretPos+_dpi_scale(1),0};
			rcCaret.bottom=rcCaret.top+font->GetLineHeight()+1;
			m_caret->SetPos(rcCaret);

			//更新输入法位置
			ZRect rc(rcCaret);
			rc.y+=_dpi_scale(CARET_OFFSET_Y);
			if (m_pManager->GetFocus() == this && SDL_IsTextInputActive())
			{
					ZScale scale = GetWindow()->GetRenderScale();
					rc.x = rc.x * scale.den / scale.num;
					rc.y = rc.y * scale.den / scale.num;
					rc.w = rc.w * scale.den / scale.num;
					rc.h = rc.h * scale.den / scale.num;
					SDL_SetTextInputRect(&rc);
			}

			ZString utf8;
			Utf16ToUtf8(&textDraw,&utf8);
			m_sText = utf8;
		}
		m_needResetText=false;
	}

	if(m_selBegin!=m_selEnd)
		PaintTextBk(render);
	else
		m_caret->Draw(render);
	

	if (m_doc.IsEmpty() && !m_tipText.IsEmpty())
	{
		color = 0xff808080;
		render->DrawText(m_sFont, m_tipText, m_nFontSize, rc, color,m_dwTextStyle);
		m_caret->Draw(render);
	}
	else
	{
		render->DrawText(m_sFont, m_sText, m_nFontSize, rc, color, m_dwTextStyle);
		if (m_editingBegin != m_editingEnd)
			PaintTextEditing(render);
	}

}



int ZTextInput::CharFromPos(POINT pt)
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	ZWString textDraw= m_doc.GetRange(m_scroll);
	ZFreeTypeFontPtr font=GetFont();
	if (font)
	{
		int width=0;
		int offset=pt.x-rc.left;
		int caret=m_scroll;
		for (int i=0;i<textDraw.GetLength();++i)
		{
			int w = font->GetWidth(m_password?m_passwordChar:textDraw[i]);
			if (width+ w > offset)
			{
				if( offset>(width+ w /2))
				{
					++caret;
				}
				break;
			}
			else
			{
				++caret;
				width+= w;
			}
		}
		return caret;
	}
	return 0;
}


POINT ZTextInput::PosFromChar(int nCh)
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	POINT pt={rc.left,rc.top};
	ZFreeTypeFontPtr font=GetFont();

	if (nCh<=m_scroll)
	{
		return pt;
	}

	int width=0;
	for (int i=m_scroll;i<nCh;++i)
	{
		width+= font->GetWidth(m_password ? m_passwordChar : m_doc.GetAt(i));
	}
	pt.x+=width;
	return pt;
}




void ZTextInput::PaintTextBk(ZRenderer* render)
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	int selBegin=m_selBegin;
	int selEnd=m_selEnd;
	if(selBegin>selEnd)
	{
		selBegin=m_selEnd;
		selEnd=m_selBegin;
	}

	if (selEnd<m_scroll)
		return;

	if (selBegin<m_scroll)
		selBegin=m_scroll;


	ZFreeTypeFontPtr font=GetFont();

	int leftOffset=0,rightOffset=0;
	for (int i=m_scroll;i<m_doc.GetLength();++i)
	{
		int w=font->GetWidth(m_password ? m_passwordChar : m_doc.GetAt(i));
		if (selBegin>i)
		{
			leftOffset+=w;
		}
		if (selEnd>i)
		{
			rightOffset+=w;
		}
	}

	ZRect rcRect;
	rcRect.x=rc.left+leftOffset;
	rcRect.y= rc.top+(rc.bottom-rc.top-font->GetLineHeight())/2;
	rcRect.h=font->GetLineHeight();
	rcRect.w=rightOffset-leftOffset;

	render->SetDrawColor(0xff9bd6ff);
	render->FillRect(rcRect);
}

void ZTextInput::PaintTextEditing(ZRenderer* render)
{
	RECT rc = m_rcItem;
	InsetRect(&rc, &m_rcTextPadding);

	int selBegin=m_editingBegin;
	int selEnd=m_editingEnd;
	if(selBegin>selEnd)
	{
		selBegin=m_editingEnd;
		selEnd=m_editingBegin;
	}

	if (selEnd<m_scroll)
		return;
	if (selBegin<m_scroll)
		selBegin=m_scroll;


	ZFreeTypeFontPtr font=GetFont();

	int leftOffset=0,rightOffset=0;
	for (int i=m_scroll;i<m_doc.GetLength();++i)
	{
		int w=font->GetWidth(m_doc.GetAt(i));
		if (selBegin>i)
		{
			leftOffset+=w;
		}
		if (selEnd>i)
		{
			rightOffset+=w;
		}
	}

	int y=rc.top+(rc.bottom-rc.top+2-font->GetLineHeight())/2+font->GetLineHeight();

	render->SetDrawColor(0xff000000);
	render->DrawLine(rc.left+leftOffset,y,rc.left+rightOffset,y);
}


}

